/**
 * Navit, a modular navigation system.
 * Copyright (C) 2005-2008 Navit Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <locale.h>
#include <gdk/gdkkeysyms.h>
#if !defined(GDK_Book) || !defined(GDK_Calendar)
#include <X11/XF86keysym.h>
#endif
#include <gtk/gtk.h>
#include "config.h"
#include "item.h"
#include "xmlconfig.h"
#include "navit.h"
#include "debug.h"
#include "gui.h"
#include "coord.h"
#include "point.h"
#include "plugin.h"
#include "graphics.h"
#include "gui_gtk.h"
#include "transform.h"
#include "config.h"
#include "callback.h"
#include "layout.h"
#include "vehicle.h"
#include "map.h"
#include "coord.h"
#include "event.h"
#include "bookmarks.h"
#include "route.h"
#include "navit_nls.h"

#ifdef USE_HILDON
#include "hildon/hildon-defines.h"
#define KEY_ZOOM_IN HILDON_HARDKEY_INCREASE
#define KEY_ZOOM_OUT HILDON_HARDKEY_DECREASE
#define KEY_UP HILDON_HARDKEY_UP
#define KEY_DOWN HILDON_HARDKEY_DOWN
#define KEY_LEFT HILDON_HARDKEY_LEFT
#define KEY_RIGHT HILDON_HARDKEY_RIGHT
#else
#ifndef GDK_Book
#define GDK_Book XF86XK_Book
#endif
#ifndef GDK_Calendar
#define GDK_Calendar XF86XK_Calendar
#endif
#define KEY_ZOOM_IN GDK_Book
#define KEY_ZOOM_OUT GDK_Calendar
#define KEY_UP GDK_Up
#define KEY_DOWN GDK_Down
#define KEY_LEFT GDK_Left
#define KEY_RIGHT GDK_Right
#endif

static gboolean keypress(GtkWidget *widget, GdkEventKey *event, struct gui_priv *this) {
    int w,h;
    struct transformation *t;
#ifdef USE_HILDON
    GtkToggleAction *action;
    gboolean *fullscreen;
#endif /*HILDON*/
    struct point p;
    if (event->type != GDK_KEY_PRESS)
        return FALSE;
    dbg(lvl_debug,"keypress 0x%x", event->keyval);
    transform_get_size(navit_get_trans(this->nav), &w, &h);
    switch (event->keyval) {
    case GDK_KP_Enter:
        gtk_menu_shell_select_first(GTK_MENU_SHELL(this->menubar), TRUE);
        break;
    case KEY_UP:
        p.x=w/2;
        p.y=0;
        navit_set_center_screen(this->nav, &p, 1);
        break;
    case KEY_DOWN:
        p.x=w/2;
        p.y=h;
        navit_set_center_screen(this->nav, &p, 1);
        break;
    case KEY_LEFT:
        p.x=0;
        p.y=h/2;
        navit_set_center_screen(this->nav, &p, 1);
        break;
    case KEY_RIGHT:
        p.x=w;
        p.y=h/2;
        navit_set_center_screen(this->nav, &p, 1);
        break;
    case KEY_ZOOM_IN:
        navit_zoom_in(this->nav, 2, NULL);
        break;
    case KEY_ZOOM_OUT:
        navit_zoom_out(this->nav, 2, NULL);
        break;
    case 'a':
        t=navit_get_trans(this->nav);
        transform_set_yaw(t, (transform_get_yaw(t)+15)%360);
        navit_draw(this->nav);
        break;
    case 'd':
        t=navit_get_trans(this->nav);
        transform_set_yaw(t, (transform_get_yaw(t)-15)%360);
        navit_draw(this->nav);
        break;
    case 'w':
        t=navit_get_trans(this->nav);
        transform_set_pitch(t, (transform_get_pitch(t)+5)%360);
        navit_draw(this->nav);
        break;
    case 'x':
        t=navit_get_trans(this->nav);
        transform_set_pitch(t, (transform_get_pitch(t)-5)%360);
        navit_draw(this->nav);
        break;
    case 'r':
        t=navit_get_trans(this->nav);
        transform_set_distance(t, (transform_get_distance(t)-5));
        navit_draw(this->nav);
        break;
    case 'f':
        t=navit_get_trans(this->nav);
        transform_set_distance(t, (transform_get_distance(t)+5));
        navit_draw(this->nav);
        break;
    case 'z':
        t=navit_get_trans(this->nav);
        transform_set_hog(t, (transform_get_hog(t)+1));
        navit_draw(this->nav);
        break;
    case 'h':
        t=navit_get_trans(this->nav);
        transform_set_hog(t, (transform_get_hog(t)-1));
        navit_draw(this->nav);
        break;
    case 't': {
        struct coord *p;
        struct pcoord pc;
        t=navit_get_trans(this->nav);
        p=transform_center(t);
        pc.pro=projection_mg;
        p->y+=50*cos(transform_get_yaw(t)*M_PI/180);
        p->x+=50*sin(transform_get_yaw(t)*M_PI/180);
        pc.x=p->x;
        pc.y=p->y;
        navit_set_center(this->nav, &pc, 1);
    }
    break;
    case 'g': {
        struct coord *p;
        struct pcoord pc;
        t=navit_get_trans(this->nav);
        p=transform_center(t);
        pc.pro=projection_mg;
        p->y-=50*cos(transform_get_yaw(t)*M_PI/180);
        p->x-=50*sin(transform_get_yaw(t)*M_PI/180);
        pc.x=p->x;
        pc.y=p->y;
        navit_set_center(this->nav, &pc, 1);
    }
    break;
#ifdef USE_HILDON
    case HILDON_HARDKEY_FULLSCREEN:
        action = GTK_TOGGLE_ACTION (gtk_action_group_get_action (this->base_group, "FullscreenAction"));

        if ( gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))) {
            fullscreen = 0;
        } else  {
            fullscreen = 1;
        }
        gtk_toggle_action_set_active (action, fullscreen);
        break;
#endif /*HILDON*/
    default:
        return FALSE;
    }
    return TRUE;
}

static int gui_gtk_set_graphics(struct gui_priv *this, struct graphics *gra) {
    GtkWidget *graphics;

    graphics=graphics_get_data(gra, "gtk_widget");
    if (! graphics)
        return 1;
    GTK_WIDGET_SET_FLAGS (graphics, GTK_CAN_FOCUS);
    gtk_widget_set_sensitive(graphics, TRUE);
    g_signal_connect(G_OBJECT(graphics), "key-press-event", G_CALLBACK(keypress), this);
    gtk_box_pack_end(GTK_BOX(this->vbox), graphics, TRUE, TRUE, 0);
    gtk_widget_show_all(graphics);
    gtk_widget_grab_focus(graphics);

    return 0;
}

static void gui_gtk_route_callback(struct gui_priv *gui) {
    struct attr route_attr;
    GtkAction *roadbookAction=gtk_ui_manager_get_action (gui->ui_manager,"/ui/ToolBar/ToolItems/Roadbook");
    if (roadbookAction) {
        if (navit_get_attr(gui->nav,attr_route,&route_attr,NULL)) {
            struct attr route_status_attr;
            if (route_get_attr(route_attr.u.route, attr_route_status,&route_status_attr,NULL) ) {
                if (route_status_attr.u.num>2) {
                    gtk_action_set_sensitive(roadbookAction,1);
                } else {
                    gtk_action_set_sensitive(roadbookAction,0);
                }
            }
        }
    }
}

static void gui_gtk_add_bookmark_do(struct gui_priv *gui) {
    struct attr attr;
    navit_get_attr(gui->nav, attr_bookmarks, &attr, NULL);
    bookmarks_add_bookmark(attr.u.bookmarks, &gui->dialog_coord, gtk_entry_get_text(GTK_ENTRY(gui->dialog_entry)));
    gtk_widget_destroy(gui->dialog_win);
}

static int gui_gtk_add_bookmark(struct gui_priv *gui, struct pcoord *c, char *description) {
    GtkWidget *button_ok,*button_cancel,*label,*vbox,*hbox;

    gui->dialog_coord=*c;
    gui->dialog_win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    vbox=gtk_vbox_new(FALSE, 0);
    gtk_container_add (GTK_CONTAINER (gui->dialog_win), vbox);
    gtk_window_set_title(GTK_WINDOW(gui->dialog_win),_("Add Bookmark"));
    gtk_window_set_wmclass (GTK_WINDOW (gui->dialog_win), "navit", "Navit");
    gtk_window_set_transient_for(GTK_WINDOW(gui->dialog_win), GTK_WINDOW(gui->win));
    gtk_window_set_modal(GTK_WINDOW(gui->dialog_win), TRUE);
    label=gtk_label_new(_("Name"));
    gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
    gui->dialog_entry=gtk_entry_new();
    gtk_entry_set_text(GTK_ENTRY(gui->dialog_entry), description);
    gtk_box_pack_start(GTK_BOX(vbox), gui->dialog_entry, TRUE, TRUE, 0);
    hbox=gtk_hbox_new(FALSE, 0);
    button_ok = gtk_button_new_from_stock (GTK_STOCK_OK);
    gtk_box_pack_start(GTK_BOX(hbox), button_ok, TRUE, TRUE, 10);
    button_cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
    gtk_box_pack_start(GTK_BOX(hbox), button_cancel, TRUE, TRUE, 10);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 10);
    gtk_widget_show_all(gui->dialog_win);
    GTK_WIDGET_SET_FLAGS (button_ok, GTK_CAN_DEFAULT);
    gtk_widget_grab_default(button_ok);
    g_signal_connect_swapped (G_OBJECT (button_cancel), "clicked", G_CALLBACK (gtk_widget_destroy),
                              G_OBJECT (gui->dialog_win));
    g_signal_connect_swapped (G_OBJECT (gui->dialog_entry), "activate", G_CALLBACK (gui_gtk_add_bookmark_do), gui);

    g_signal_connect_swapped(G_OBJECT (button_ok), "clicked", G_CALLBACK (gui_gtk_add_bookmark_do), gui);

    return 1;
}

struct gui_methods gui_gtk_methods = {
    NULL,
    gui_gtk_popup_new,
    gui_gtk_set_graphics,
    NULL,
    gui_gtk_datawindow_new,
    gui_gtk_add_bookmark,
};

static gboolean gui_gtk_delete(GtkWidget *widget, GdkEvent *event, struct navit *nav) {
    /* FIXME remove attr_navit callback */
    navit_destroy(nav);
    exit(0);

    return TRUE;
}

static void gui_gtk_toggle_init(struct gui_priv *this) {
    struct attr attr;
    GtkToggleAction *toggle_action;

    if (navit_get_attr(this->nav, attr_cursor, &attr, NULL)) {
        toggle_action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(this->base_group, "CursorAction"));
        gtk_toggle_action_set_active(toggle_action, attr.u.num);
    } else {
        dbg(lvl_warning, "Unable to locate CursorAction");
    }
    if (navit_get_attr(this->nav, attr_orientation, &attr, NULL)) {
        toggle_action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(this->base_group, "OrientationAction"));
        gtk_toggle_action_set_active(toggle_action, attr.u.num != -1);
    } else {
        dbg(lvl_error, "Unable to locate OrientationAction");
    }
    if (navit_get_attr(this->nav, attr_tracking, &attr, NULL)) {
        toggle_action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(this->base_group, "TrackingAction"));
        gtk_toggle_action_set_active(toggle_action, attr.u.num);
    } else {
        dbg(lvl_error, "Unable to locate TrackingAction");
    }
}

struct action_cb_data {
    struct gui_priv *gui;
    struct attr attr;
};

static void gui_gtk_action_activate(GtkAction *action, struct action_cb_data *data) {
    if(data->attr.type == attr_destination) {
        char * label;
        g_object_get(G_OBJECT(action), "label", &label,NULL);
        navit_populate_search_results_map(data->gui->nav, NULL, NULL); 	/* Remove any highlighted point on the map */
        navit_set_destination(data->gui->nav, data->attr.u.pcoord, label, 1);
        g_free(label);
    }
}

struct gui_menu_info {
    guint merge_id;
    GtkAction *action;
};

static void gui_gtk_del_menu(struct gui_priv *this, struct gui_menu_info *meninfo) {
    gtk_action_group_remove_action(this->dyn_group, meninfo->action);
    gtk_ui_manager_remove_ui(this->ui_manager, meninfo->merge_id);
}

static struct gui_menu_info gui_gtk_add_menu(struct gui_priv *this, char *name, char *label, char *path, int submenu,
        struct action_cb_data *data) {
    struct gui_menu_info meninfo;
    GtkAction *action;
    guint merge_id;

    action=gtk_action_new(name, label, NULL, NULL);
    meninfo.action = action;
    if (data)
        g_signal_connect(action, "activate", G_CALLBACK(gui_gtk_action_activate), data);
    gtk_action_group_add_action(this->dyn_group, action);
    merge_id =gtk_ui_manager_new_merge_id(this->ui_manager);
    meninfo.merge_id = merge_id;
    gtk_ui_manager_add_ui(this->ui_manager, merge_id, path, name, name,
                          submenu ? GTK_UI_MANAGER_MENU : GTK_UI_MANAGER_MENUITEM, FALSE);

    return meninfo;
}

static void gui_gtk_action_toggled(GtkToggleAction *action, struct action_cb_data *data) {
    struct attr active;
    active.type=attr_active;
    active.u.num=gtk_toggle_action_get_active(action);
    map_set_attr(data->attr.u.map, &active);
    navit_draw(data->gui->nav);
}

static void gui_gtk_add_toggle_menu(struct gui_priv *this, char *name, char *label, char *path,
                                    struct action_cb_data *data, gboolean active) {
    GtkToggleAction *toggle_action;
    guint merge_id;

    toggle_action=gtk_toggle_action_new(name, label, NULL, NULL);
    gtk_toggle_action_set_active(toggle_action, active);
    g_signal_connect(GTK_ACTION(toggle_action), "toggled", G_CALLBACK(gui_gtk_action_toggled), data);
    gtk_action_group_add_action(this->dyn_group, GTK_ACTION(toggle_action));
    merge_id=gtk_ui_manager_new_merge_id(this->ui_manager);
    gtk_ui_manager_add_ui(this->ui_manager, merge_id, path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
}

static void gui_gtk_action_changed(GtkRadioAction *action, GtkRadioAction *current, struct action_cb_data *data) {
    if (action == current) {
        navit_set_attr(data->gui->nav, &data->attr);
    }
}

static struct gui_menu_info gui_gtk_add_radio_menu(struct gui_priv *this, char *name, char *label, char *path,
        struct action_cb_data *data, GSList **g) {
    struct gui_menu_info meninfo;
    GtkRadioAction *radio_action;
    guint merge_id;

    radio_action=gtk_radio_action_new(name, label, NULL, NULL, 0);
    meninfo.action = (GtkAction *)radio_action;
    gtk_radio_action_set_group(radio_action, *g);
    *g=gtk_radio_action_get_group(radio_action);
    g_signal_connect(GTK_ACTION(radio_action), "changed", G_CALLBACK(gui_gtk_action_changed), data);
    gtk_action_group_add_action(this->dyn_group, GTK_ACTION(radio_action));
    merge_id=gtk_ui_manager_new_merge_id(this->ui_manager);
    meninfo.merge_id = merge_id;
    gtk_ui_manager_add_ui(this->ui_manager, merge_id, path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE);

    return meninfo;
}

static void gui_gtk_layouts_init(struct gui_priv *this) {
    struct attr_iter *iter;
    struct attr attr;
    struct action_cb_data *data;
    int count=0;
    char *name;

    iter=navit_attr_iter_new();
    while(navit_get_attr(this->nav, attr_layout, &attr, iter)) {
        name=g_strdup_printf("Layout %d", count++);
        data=g_new(struct action_cb_data, 1);
        data->gui=this;
        data->attr.type=attr_layout;
        data->attr.u.layout=attr.u.layout;
        gui_gtk_add_radio_menu(this, name, attr.u.layout->name, "/ui/MenuBar/Map/Layout/LayoutMenuAdditions", data,
                               &this->layout_group);
        g_free(name);
    }
    navit_attr_iter_destroy(iter);
}

static void gui_gtk_projections_init(struct gui_priv *this) {
    struct action_cb_data *data;

    data=g_new(struct action_cb_data, 1);
    data->gui=this;
    data->attr.type=attr_projection;
    data->attr.u.projection=projection_mg;
    gui_gtk_add_radio_menu(this, "Projection mg", "Map & Guide", "/ui/MenuBar/Map/Projection/ProjectionMenuAdditions", data,
                           &this->projection_group);

    data=g_new(struct action_cb_data, 1);
    data->gui=this;
    data->attr.type=attr_projection;
    data->attr.u.projection=projection_garmin;
    gui_gtk_add_radio_menu(this, "Projection garmin", "Garmin", "/ui/MenuBar/Map/Projection/ProjectionMenuAdditions", data,
                           &this->projection_group);
}

static void gui_gtk_vehicles_update(struct gui_priv *this) {
    struct attr_iter *iter;
    struct attr attr,vattr;
    struct action_cb_data *data;
    int count=0;
    char *name;
    GList *curr;
    struct gui_menu_info *meninfo;
    dbg(lvl_debug,"enter");

    curr = g_list_first(this->vehicle_menuitems);

    while (curr) {
        gui_gtk_del_menu(this, (struct gui_menu_info *)curr->data);
        g_free((struct gui_menu_info *)curr->data);
        curr = g_list_next(curr);
    };

    g_list_free(this->vehicle_menuitems);
    this->vehicle_menuitems = NULL;

    iter=navit_attr_iter_new();
    while(navit_get_attr(this->nav, attr_vehicle, &attr, iter)) {
        vehicle_get_attr(attr.u.vehicle, attr_name, &vattr, NULL);
        name=g_strdup_printf("Vehicle %d", count++);
        data=g_new(struct action_cb_data, 1);
        data->gui=this;
        data->attr.type=attr_vehicle;
        data->attr.u.vehicle=attr.u.vehicle;
        meninfo = g_new(struct gui_menu_info, 1);
        *meninfo = gui_gtk_add_radio_menu(this, name, vattr.u.str, "/ui/MenuBar/Map/Vehicle/VehicleMenuAdditions", data,
                                          &this->vehicle_group);
        this->vehicle_menuitems = g_list_prepend(this->vehicle_menuitems, meninfo);
        g_free(name);
    }
    navit_attr_iter_destroy(iter);
}

static void gui_gtk_vehicles_init(struct gui_priv *this) {
    navit_add_callback(this->nav, callback_new_attr_1(callback_cast(gui_gtk_vehicles_update), attr_vehicle, this));
    gui_gtk_vehicles_update(this);
}

static void gui_gtk_maps_init(struct gui_priv *this) {
    struct attr_iter *iter;
    struct attr attr,active,type,data;
    struct action_cb_data *cb_data;
    int count=0;
    char *name, *label;

    iter=navit_attr_iter_new();
    while(navit_get_attr(this->nav, attr_map, &attr, iter)) {
        name=g_strdup_printf("Map %d", count++);
        if (! map_get_attr(attr.u.map, attr_type, &type, NULL))
            type.u.str="";
        if (! map_get_attr(attr.u.map, attr_data, &data, NULL))
            data.u.str="";
        label=g_strdup_printf("%s:%s", type.u.str, data.u.str);
        cb_data=g_new(struct action_cb_data, 1);
        cb_data->gui=this;
        cb_data->attr.type=attr_map;
        cb_data->attr.u.map=attr.u.map;
        if (! map_get_attr(attr.u.map, attr_active, &active, NULL))
            active.u.num=1;
        gui_gtk_add_toggle_menu(this, name, label, "/ui/MenuBar/Map/MapMenuAdditions", cb_data, active.u.num);
        g_free(name);
        g_free(label);
    }
    navit_attr_iter_destroy(iter);

}

static void gui_gtk_destinations_update(struct gui_priv *this) {
    GList *curr;
    struct attr attr;
    struct action_cb_data *data;
    struct map_rect *mr=NULL;
    struct item *item;
    struct gui_menu_info *meninfo;
    struct coord c;
    int count=0;
    char *name, *label;

    curr = g_list_first(this->dest_menuitems);

    while (curr) {
        gui_gtk_del_menu(this, (struct gui_menu_info *)curr->data);
        g_free((struct gui_menu_info *)curr->data);
        curr = g_list_next(curr);
    };

    g_list_free(this->dest_menuitems);
    this->dest_menuitems = NULL;

    if(navit_get_attr(this->nav, attr_former_destination_map, &attr, NULL) && attr.u.map
            && (mr=map_rect_new(attr.u.map, NULL))) {
        while ((item=map_rect_get_item(mr))) {
            if (item->type != type_former_destination) continue;
            name=g_strdup_printf("Destination %d", count++);
            item_attr_get(item, attr_label, &attr);
            label=attr.u.str;
            item_coord_get(item, &c, 1);
            data=g_new(struct action_cb_data, 1);
            data->gui=this;
            data->attr.type=attr_destination;
            data->attr.u.pcoord=g_new(struct pcoord, 1);
            data->attr.u.pcoord->pro=projection_mg;
            data->attr.u.pcoord->x=c.x;
            data->attr.u.pcoord->y=c.y;

            meninfo = g_new(struct gui_menu_info, 1);
            *meninfo = gui_gtk_add_menu(this, name, label, "/ui/MenuBar/Route/FormerDestinations/FormerDestinationMenuAdditions",0,
                                        data);
            this->dest_menuitems = g_list_prepend(this->dest_menuitems, meninfo);
            g_free(name);
        }
        map_rect_destroy(mr);
    }
}

static void gui_gtk_destinations_init(struct gui_priv *this) {
    navit_add_callback(this->nav, callback_new_attr_1(callback_cast(gui_gtk_destinations_update), attr_destination, this));
    gui_gtk_destinations_update(this);
}

static void gui_gtk_bookmarks_update(struct gui_priv *this) {
    GList *curr;
    struct attr attr;
    struct action_cb_data *data;
    struct map_rect *mr=NULL;
    struct gui_menu_info *meninfo;
    struct item *item;
    struct coord c;
    int count=0;
    char *parent, *name, *label, *label_full, *menu_label, *tmp_parent, *s;
    GHashTable *hash;

    curr = g_list_first(this->bookmarks_menuitems);

    while (curr) {
        gui_gtk_del_menu(this, (struct gui_menu_info *)curr->data);
        g_free((struct gui_menu_info *)curr->data);
        curr = g_list_next(curr);
    };

    g_list_free(this->bookmarks_menuitems);
    this->bookmarks_menuitems = NULL;

    if(navit_get_attr(this->nav, attr_bookmark_map, &attr, NULL) && attr.u.map && (mr=map_rect_new(attr.u.map, NULL))) {
        hash=g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
        while ((item=map_rect_get_item(mr))) {
            if (item->type != type_bookmark) continue;
            item_attr_get(item, attr_label, &attr);
            label_full=attr.u.str;
            item_coord_get(item, &c, 1);
            menu_label=g_malloc(strlen(label_full)+1);
            label=label_full;
            parent=g_strdup("/ui/MenuBar/Route/Bookmarks/BookmarkMenuAdditions");
            while ((s=strchr(label, '/'))) {
                strcpy(menu_label, label_full);
                menu_label[s-label_full]='\0';
                if ((tmp_parent=g_hash_table_lookup(hash, menu_label))) {
                    tmp_parent=g_strdup(tmp_parent);
                } else {
                    name=g_strdup_printf("Bookmark %d", count++);
                    meninfo = g_new(struct gui_menu_info, 1);
                    *meninfo = gui_gtk_add_menu(this, name, menu_label+(label-label_full),parent,1,NULL);
                    this->bookmarks_menuitems = g_list_prepend(this->bookmarks_menuitems, meninfo);
                    tmp_parent=g_strdup_printf("%s/%s", parent, name);
                    g_hash_table_insert(hash, g_strdup(menu_label), g_strdup(tmp_parent));
                    g_free(name);
                }
                g_free(parent);
                parent=tmp_parent;
                label=s+1;
            }
            g_free(menu_label);
            data=g_new(struct action_cb_data, 1);
            data->gui=this;
            data->attr.type=attr_destination;
            data->attr.u.pcoord=g_new(struct pcoord, 1);
            data->attr.u.pcoord->pro=projection_mg;
            data->attr.u.pcoord->x=c.x;
            data->attr.u.pcoord->y=c.y;
            name=g_strdup_printf("Bookmark %d", count++);
            meninfo = g_new(struct gui_menu_info, 1);
            *meninfo = gui_gtk_add_menu(this, name, label, parent,0,data);
            this->bookmarks_menuitems = g_list_prepend(this->bookmarks_menuitems, meninfo);
            g_free(name);
            g_free(parent);
        }
        g_hash_table_destroy(hash);
    }
}

static void gui_gtk_bookmarks_init(struct gui_priv *this) {
    struct attr attr;
    navit_get_attr(this->nav, attr_bookmarks, &attr, NULL);
    bookmarks_add_callback(attr.u.bookmarks, callback_new_attr_1(callback_cast(gui_gtk_bookmarks_update), attr_bookmark_map,
                           this));
    gui_gtk_bookmarks_update(this);
}

static void gui_gtk_init(struct gui_priv *this, struct navit *nav) {

    struct attr route_attr;

    gui_gtk_toggle_init(this);
    gui_gtk_layouts_init(this);
    gui_gtk_projections_init(this);
    gui_gtk_vehicles_init(this);
    gui_gtk_maps_init(this);
    gui_gtk_destinations_init(this);
    gui_gtk_bookmarks_init(this);

    if (navit_get_attr(nav,attr_route,&route_attr,NULL)) {
        struct attr callback;
        callback.type=attr_callback;
        callback.u.callback=callback_new_attr_1(callback_cast(gui_gtk_route_callback), attr_route_status, this);
        route_add_attr(route_attr.u.route, &callback);
    }
    gui_gtk_route_callback(this); //Set initial state
}

static struct gui_priv *gui_gtk_new(struct navit *nav, struct gui_methods *meth, struct attr **attrs, struct gui *gui) {
    struct gui_priv *this;
    int w=792, h=547;
    char *cp = getenv("NAVIT_XID");
    unsigned xid = 0;
    struct attr *attr;
    GtkWidget *widget;
    int fullscreen = 0;

    if (! event_request_system("glib","gui_gtk_new"))
        return NULL;

    if (cp) {
        xid = strtol(cp, NULL, 0);
    }

    this=g_new0(struct gui_priv, 1);
    this->nav=nav;

    attr = attr_search(attrs, NULL, attr_menubar);
    if (attr) {
        this->menubar_enable=attr->u.num;
    } else {
        this->menubar_enable=1;
    }
    attr=attr_search(attrs, NULL, attr_toolbar);
    if (attr) {
        this->toolbar_enable=attr->u.num;
    } else {
        this->toolbar_enable=1;
    }
    attr=attr_search(attrs, NULL, attr_statusbar);
    if (attr) {
        this->statusbar_enable=attr->u.num;
    } else {
        this->statusbar_enable=1;
    }

    *meth=gui_gtk_methods;

    if (!xid)
        this->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    else
        this->win = gtk_plug_new(xid);


    g_signal_connect(G_OBJECT(this->win), "delete-event", G_CALLBACK(gui_gtk_delete), nav);
    this->vbox = gtk_vbox_new(FALSE, 0);
    gtk_window_set_default_size(GTK_WINDOW(this->win), w, h);
    gtk_window_set_title(GTK_WINDOW(this->win), "Navit");
    gtk_window_set_wmclass (GTK_WINDOW (this->win), "navit", "Navit");
    gtk_widget_realize(this->win);
    gui_gtk_ui_init(this);
    if (this->menubar_enable) {
        widget=gtk_ui_manager_get_widget(this->ui_manager, "/ui/MenuBar");
        GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS);
        gtk_box_pack_start (GTK_BOX(this->vbox), widget, FALSE, FALSE, 0);
        gtk_widget_show (widget);
        this->menubar=widget;
    }
    if (this->toolbar_enable) {
        widget=gtk_ui_manager_get_widget(this->ui_manager, "/ui/ToolBar");
        GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS);
        gtk_box_pack_start (GTK_BOX(this->vbox), widget, FALSE, FALSE, 0);
        gtk_widget_show (widget);
    }
    if (this->statusbar_enable) {
        this->statusbar=gui_gtk_statusbar_new(this);
    }
    gtk_window_add_accel_group (GTK_WINDOW (this->win),
                                gtk_ui_manager_get_accel_group(this->ui_manager));
    gtk_container_add(GTK_CONTAINER(this->win), this->vbox);
    gtk_widget_show_all(this->win);


    navit_add_callback(nav, callback_new_attr_1(callback_cast(gui_gtk_init), attr_navit, this));

    if ((attr=attr_search(attrs, NULL, attr_fullscreen)))
        fullscreen=attr->u.num;

    if (fullscreen) {
        GtkToggleAction *action;
        action = GTK_TOGGLE_ACTION (gtk_action_group_get_action (this->base_group, "FullscreenAction"));
        gtk_toggle_action_set_active (action, fullscreen);
    }

    return this;
}

static int gtk_argc;
static char **gtk_argv= {NULL};

void plugin_init(void) {
    gtk_init(&gtk_argc, &gtk_argv);
    gtk_set_locale();
#ifdef HAVE_API_WIN32
    setlocale(LC_NUMERIC,"C");
#endif


    plugin_register_category_gui("gtk", gui_gtk_new);
}
